package com.modular.extra.fm.data.action;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.BiMap;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.StaticLog;
import com.c.opt.query.ComOptQueryWrapper;
import com.modular.extra.fm.FactoryFmOpt;
import com.modular.extra.fm.data.bo.DataCheckBo;
import com.modular.extra.fm.site.bean.FmSiteInfoDo;
import ntd.action.ActionAbs;
import ntd.action.bo.ActionInBo;
import ntd.action.bo.ActionInfo;
import ntd.action.bo.ActionOutBo;
import ntd.util.frame.task.handle.fm.bo.FmDataRecordBo;

import java.util.*;
import java.util.concurrent.CountDownLatch;

public class DataCheckAction extends ActionAbs {

    @Override
    public ActionInfo info() {
        return new ActionInfo("富铭 - 数据检测", "action-fm-data-check", this);
    }

    @Override
    public ActionOutBo handle() {
        int dayNum = NumberUtil.parseInt(Opt.ofBlankAble(actionInBo.getParamStr("dayNum")).orElse("7"));
        // 检测数据缺失 - 默认近一个月
        String date = DateUtil.offsetDay(DateUtil.date(), -dayNum).toDateStr();
        ComOptQueryWrapper queryWrapper = FactoryFmOpt.OPT_DATA_RECORD.queryWrapper();
        queryWrapper.ge("create_time", date);
        queryWrapper.eq("cn", "61");
        Opt.ofBlankAble(actionInBo.getParamStr("mn")).ifPresent(mn -> queryWrapper.eq("mn", mn));
        List<FmDataRecordBo> fmDataRecordBos = FactoryFmOpt.OPT_DATA_RECORD.listBean(queryWrapper);

        Map<String, List<FmDataRecordBo>> map = CollStreamUtil.groupByKey(fmDataRecordBos, fmDataRecordBo ->
                StrUtil.format("{}({})", fmDataRecordBo.getMn(), fmDataRecordBo.getCn()));

        List<FmSiteInfoDo> fmSiteInfoDos = FactoryFmOpt.FM_SITE_OPT.query().listBean();
        Map<String, FmSiteInfoDo> siteInfoDoMap = CollStreamUtil.toIdentityMap(fmSiteInfoDos, FmSiteInfoDo::getMn);

        List<DataCheckBo> dataCheckBos = new ArrayList<>();

        CountDownLatch countDownLatch = ThreadUtil.newCountDownLatch(map.size());
        for (Map.Entry<String, List<FmDataRecordBo>> entry : map.entrySet()) {
            ThreadUtil.execute(() -> {
                Opt.ofNullable(checkData(entry.getValue())).ifPresent(dataCheckBo -> {
                    FmSiteInfoDo fmSiteInfoDo = siteInfoDoMap.get(dataCheckBo.getCode());
                    if (ObjectUtil.isNotNull(fmSiteInfoDo)) {
                        dataCheckBo.setSiteName(fmSiteInfoDo.getSiteName());
                    }
                    dataCheckBos.add(dataCheckBo);
                });
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return new ActionOutBo(dataCheckBos);
    }

    /**
     * 检查缺失数据
     *
     * @param fmDataRecordBos
     * @return
     */
    public DataCheckBo checkData(List<FmDataRecordBo> fmDataRecordBos) {
        if (CollectionUtil.isEmpty(fmDataRecordBos)) {
            return null;
        }

        // 时间排序 - 修改原集合
        ListUtil.sort(fmDataRecordBos, (o1, o2) -> DateUtil.compare(DateUtil.parse(o1.getCreateTime()),
                DateUtil.parse(o2.getCreateTime())));

        // 起始时间
        FmDataRecordBo fmDataRecordBo = fmDataRecordBos.get(0);

        // 当前时间毫秒值
        long currTime = 0;

        // 时间区间频率存储
        BiMap<Long, Long> longMap = new BiMap<>(new HashMap<>());

        // 时间区间计算及存储 - 毫秒
        for (FmDataRecordBo dataRecordBo : fmDataRecordBos) {
            long time = DateUtil.parse(dataRecordBo.getCreateTime()).getTime();
            long l = time - currTime;
            if (l > 0) {
                Long orDefault = longMap.getOrDefault(l, 0l);
                longMap.put(l, orDefault + 1);
                currTime = time;
            }
        }

        // 获取理想间隔时间
        Long intervalTime = longMap.getKey(CollectionUtil.max(longMap.values()));
//        StaticLog.info("理想间隔时间[{}]", intervalTime);

        // 获取起始时间
        Long startTime = DateUtil.parse(fmDataRecordBo.getCreateTime()).getTime();
        DateTime dateTimeOf2Hour = DateUtil.offsetHour(DateUtil.date(), -2);

        // 计算区间时间
        Long betweenTime = DateUtil.date().getTime() - startTime;

        if (intervalTime < 1) {
            StaticLog.info(String.valueOf(intervalTime));
            StaticLog.info(JSONUtil.toJsonStr(longMap));
            return null;
        }

        // 计算间隔次数
        int num = NumberUtil.div(betweenTime, intervalTime).intValue();

        // 构建间隔时间表
        List<Long> intervalTimes = new ArrayList<>();

        for (int i = 0; i < num; i++) {
            intervalTimes.add(startTime + (i * intervalTime));
        }

        // 对比缺失数据
        for (FmDataRecordBo dataRecordBo : fmDataRecordBos) {
            Long time = DateUtil.parse(dataRecordBo.getCreateTime()).getTime();
            clear(intervalTimes, time);
        }

        DataCheckBo dataCheckBo = new DataCheckBo();
        dataCheckBo.setMissTimes(CollStreamUtil.toList(intervalTimes, l -> {
            DateTime date = DateUtil.date(l);
            if (DateUtil.compare(dateTimeOf2Hour, date) < 0) {
                return null;
            }
            return date.toString();
        }));
        dataCheckBo.setCode(fmDataRecordBo.getMn());
        dataCheckBo.setShouldDataNum(num);
        dataCheckBo.setDataNum(dataCheckBo.getMissTimes().size());
        if (num > 0) {
            dataCheckBo.setDataIntegrity(NumberUtil.sub(1, NumberUtil.div(dataCheckBo.getMissTimes().size(), num, 4)));
        }

        return dataCheckBo;
    }

    public void clear(List<Long> intervalTimes, Long time) {
        if (CollectionUtil.contains(intervalTimes, time)) {
            intervalTimes.remove(time);
            clear(intervalTimes, time);
        } else {
//                StaticLog.info("意外数据[{}]", JSONUtil.toJsonStr(dataRecordBo));
        }
    }

    public static void main(String[] args) {
        ActionOutBo handle = new DataCheckAction().execute(new ActionInBo());
        JSONArray objects = JSONUtil.parseArray(handle.getData());
        for (int i = 0; i < objects.size(); i++) {
            JSONObject jsonObject = objects.getJSONObject(i);
            if (jsonObject.getJSONArray("missTimes").contains("2023-08-02 09:00:00")) {
                FmSiteInfoDo fmSiteInfoDo = FactoryFmOpt.FM_SITE_OPT.getByMn(jsonObject.getStr("code"));
                if (ListUtil.of(31, 32, 33, 36, 34, 35).contains(fmSiteInfoDo.getSiteType2())) {
                    System.out.println(jsonObject.getStr("code"));
                }
            }
        }


    }
}
